package com.y2.sparrow.generator;

import com.mybatisflex.codegen.Generator;
import com.mybatisflex.codegen.config.GlobalConfig;
import com.zaxxer.hikari.HikariDataSource;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

/**
 * 代码生成器
 * <p>
 * {@linkplain #instance() 创建 CodeGenerator 实例}
 * <p>
 * 数据库相关配置
 * {@linkplain #dataSource(String, String, String) 配置数据源}
 * {@linkplain #dataSource(String, int, String, String, String) 配置数据源}
 * {@linkplain #table(String...) 配置哪些表需要生成代码，不配置默认生成全部表}
 * {@linkplain #ignoreColumns(String...) 配置忽略数据库列}
 * {@linkplain #version(String) 配置版本号字段}
 * {@linkplain #isDelete(String) 配置逻辑删除字段}
 * <p>
 * 项目相关配置
 * {@linkplain #basePackage(String) 配置根包名}
 * {@linkplain #modulePackage(String, String, String, String) 配置模块包名}
 * {@linkplain #code(boolean, boolean, boolean, boolean, boolean) 配置要生成哪些模块的代码，默认全都生成（entity,mapper,xml,service,controller）}
 * {@linkplain #entitySuperClass(String) 配置 entity 父类}
 * {@linkplain #lombok(boolean) 配置是否将 entity 设置为 lombok 风格}
 * <p>
 * doc相关配置
 * {@linkplain #docAuthor(String) 配置注释中的作者}
 * <p>
 * 生成模板相关配置
 * {@linkplain #templateBasePath(String) 配置代码生成模板根包}
 * {@linkplain #templateParam(Map) 配置模板参数/变量}
 * <p>
 * 文件相关配置
 * {@linkplain #path(String) 配置代码生成路径}
 * {@linkplain #path(String, String, String, String, String, String) 配置代码生成路径}
 *
 * @author shaoyuyao
 * @date 2024/12/25 15:48
 */
public class CodeGenerator {
    /**
     * 数据库连接盒子
     */
    private ConnectionBox connectionBox;

    /**
     * 代码生成规则配置
     */
    private GlobalConfig globalConfig;

    /**
     * 代码生成器默认模板位置
     */
    private final String DEFAULT_TEMPLATE_PATH = System.getProperty("user.dir") + "\\src\\main\\resources\\templates\\enjoy\\default";

    public CodeGenerator() {
        // 初始化生成器配置内容
        initGlobalConfig();
    }

    /**
     * 创建 CodeGenerator 实例
     *
     * @return CodeGenerator 实例
     */
    public static CodeGenerator instance() {
        return new CodeGenerator();
    }

    /**
     * 配置数据源
     *
     * @param schemaName 数据库名
     * @param username   用户名
     * @param password   密码
     * @return CodeGenerator 实例
     */
    public CodeGenerator dataSource(String schemaName, String username, String password) {
        return dataSource("127.0.0.1", 3306, schemaName, username, password);
    }

    /**
     * 配置数据源
     *
     * @param ip         ip
     * @param port       端口号
     * @param schemaName 数据库名
     * @param username   用户名
     * @param password   密码
     * @return CodeGenerator 实例
     */
    public CodeGenerator dataSource(String ip, int port, String schemaName, String username, String password) {
        this.connectionBox = new ConnectionBox(ip, port, schemaName, username, password);
        return this;
    }

    /**
     * 配置哪些表需要生成代码，不配置默认生成全部表
     *
     * @param tableNames 数据库表名
     * @return CodeGenerator 实例
     */
    public CodeGenerator table(String... tableNames) {
        this.globalConfig.setGenerateTable(tableNames);
        return this;
    }

    /**
     * 配置忽略数据库列
     *
     * @param columns 数据库列名
     * @return CodeGenerator 实例
     */
    public CodeGenerator ignoreColumns(String... columns) {
        this.globalConfig.getStrategyConfig().setIgnoreColumns(columns);
        return this;
    }

    /**
     * 配置版本号字段
     *
     * @param column 版本号字段名
     * @return CodeGenerator 实例
     */
    public CodeGenerator version(String column) {
        this.globalConfig.setVersionColumn(column);
        return this;
    }

    /**
     * 配置逻辑删除字段
     *
     * @param column 逻辑删除字段名
     * @return CodeGenerator 实例
     */
    public CodeGenerator isDelete(String column) {
        this.globalConfig.setLogicDeleteColumn(column);
        return this;
    }

    /**
     * 配置根包名
     *
     * @param basePackageName 根包名
     * @return CodeGenerator 实例
     */
    public CodeGenerator basePackage(String basePackageName) {
        this.globalConfig.setBasePackage(basePackageName);
        return this;
    }

    /**
     * 配置模块包名
     *
     * @param entity     entity 包名
     * @param mapper     mapper 包名
     * @param service    service 包名
     * @param controller controller 包名
     * @return
     */
    public CodeGenerator modulePackage(String entity, String mapper, String service, String controller) {
        if (null != entity && !"".equals(entity.trim())) {
            this.globalConfig.setEntityPackage(this.globalConfig.getBasePackage() + "." + entity + ".entity");
        }
        if (null != mapper && !"".equals(mapper.trim())) {
            this.globalConfig.setMapperPackage(this.globalConfig.getBasePackage() + "." + mapper + ".mapper");
        }
        if (null != service && !"".equals(service.trim())) {
            this.globalConfig.setServicePackage(this.globalConfig.getBasePackage() + "." + service + ".service");
        }
        if (null != controller && !"".equals(controller.trim())) {
            this.globalConfig.setControllerPackage(this.globalConfig.getBasePackage() + "." + controller + ".controller");
        }

        return this;
    }

    /**
     * 配置要生成哪些模块的代码，默认全都生成（entity,mapper,xml,service,controller）
     *
     * @param entity     是否要生成 entity 代码
     * @param mapper     是否要生成 mapper 代码
     * @param xml        是否要生成 xml 代码
     * @param service    是否要生成 service 代码
     * @param controller 是否要生成 controller 代码
     * @return CodeGenerator 实例
     */
    public CodeGenerator code(boolean entity, boolean mapper, boolean xml,
                              boolean service, boolean controller) {
        this.globalConfig.setEntityGenerateEnable(entity);
        this.globalConfig.setMapperGenerateEnable(mapper);
        this.globalConfig.setMapperXmlGenerateEnable(xml);
        this.globalConfig.setServiceGenerateEnable(service);
        this.globalConfig.setServiceImplGenerateEnable(service);
        this.globalConfig.setControllerGenerateEnable(controller);
        return this;
    }

    /**
     * 配置 entity 父类
     *
     * @param fullClassName 全限定类名，例如：fullClassName
     * @return CodeGenerator 实例
     */
    public CodeGenerator entitySuperClass(String fullClassName) {
        int lastDotIndex = fullClassName.lastIndexOf('.');
        String className = fullClassName.substring(lastDotIndex + 1);
        this.globalConfig.getCustomConfig().put("entitySuperClassFullName", fullClassName);
        this.globalConfig.getCustomConfig().put("entitySuperClassName", className);
        return this;
    }

    /**
     * 配置是否将 entity 设置为 lombok 风格
     *
     * @param lombok 是否将 entity 设置为 lombok 风格
     * @return CodeGenerator 实例
     */
    public CodeGenerator lombok(boolean lombok) {
        this.globalConfig.setEntityWithLombok(lombok);
        return this;
    }

    /**
     * 配置注释中的作者
     *
     * @param author 作者
     * @return CodeGenerator 实例
     */
    public CodeGenerator docAuthor(String author) {
        this.globalConfig.setAuthor(author);
        return this;
    }

    /**
     * 配置代码生成模板根包
     *
     * @param templateBasePath 代码生成模板根包
     * @return CodeGenerator 实例
     */
    public CodeGenerator templateBasePath(String templateBasePath) {
        this.globalConfig.getTemplateConfig().setEntity(templateBasePath + "/entityOrBase.tpl");
        this.globalConfig.getTemplateConfig().setMapper(templateBasePath + "/mapper.tpl");
        this.globalConfig.getTemplateConfig().setService(templateBasePath + "/service.tpl");
        this.globalConfig.getTemplateConfig().setServiceImpl(templateBasePath + "/serviceImpl.tpl");
        this.globalConfig.getTemplateConfig().setController(templateBasePath + "/controller.tpl");
        this.globalConfig.getTemplateConfig().setMapperXml(templateBasePath + "/mapperXml.tpl");
        return this;
    }

    /**
     * 配置模板参数/变量
     *
     * @param param 模板参数/变量
     * @return CodeGenerator 实例
     */
    public CodeGenerator templateParam(Map<String, Object> param) {
        this.globalConfig.setCustomConfig(param);
        return this;
    }

    /**
     * 配置代码生成路径
     *
     * @param generatePath 代码生成路径
     * @return CodeGenerator 实例
     */
    public CodeGenerator path(String generatePath) {
        this.globalConfig.setSourceDir(generatePath);
        return this;
    }

    /**
     * 配置代码生成路径
     *
     * @param entityPath      entity 生成路径
     * @param mapperPath      mapper 生成路径
     * @param xmlPath         mapper-xml 生成路径
     * @param servicePath     service 生成路径
     * @param serviceImplPath service-impl 生成路径
     * @param controllerPath  controller 生成路径
     * @return CodeGenerator 实例
     */
    public CodeGenerator path(String entityPath, String mapperPath, String xmlPath,
                              String servicePath, String serviceImplPath, String controllerPath) {
        this.globalConfig.getEntityConfig().setSourceDir(entityPath);
        this.globalConfig.getMapperConfig().setSourceDir(mapperPath);
        this.globalConfig.setMapperXmlPath(xmlPath);
        this.globalConfig.getServiceConfig().setSourceDir(servicePath);
        this.globalConfig.getServiceImplConfig().setSourceDir(serviceImplPath);
        this.globalConfig.getControllerConfig().setSourceDir(controllerPath);
        return this;
    }

    /**
     * 初始化生成器配置内容
     *
     * @return GlobalConfig 实例
     */
    private void initGlobalConfig() {
        this.globalConfig = new GlobalConfig();

        // 设置根包
        this.globalConfig.setBasePackage("com.test");

        // 生成 entity
        this.globalConfig.setEntityGenerateEnable(true);
        // 生成 lombok 风格 entity
        this.globalConfig.setEntityWithLombok(true);
        // 生成 mapper
        this.globalConfig.setMapperGenerateEnable(true);
        // 生成 xml
        this.globalConfig.setMapperXmlGenerateEnable(true);
        // 生成 service
        this.globalConfig.setServiceGenerateEnable(true);
        // 生成 controller
        this.globalConfig.setControllerGenerateEnable(true);

        // 代码生成器默认模板
        templateBasePath(DEFAULT_TEMPLATE_PATH);

        // 代码生成器模板参数
        Map<String, Object> templateParam = new HashMap<>();
        templateParam.put("date", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm")));
        this.globalConfig.setCustomConfig(templateParam);

        // 设置项目的JDK版本，项目的JDK为14及以上时建议设置该项，小于14则可以不设置
        this.globalConfig.setEntityJdkVersion(17);
    }

    public void generate() {
        // 创建数据源
        HikariDataSource dataSource = connectionBox.createDataSource();

        // 通过 datasource 和 this.globalConfig 创建代码生成器
        Generator generator = new Generator(dataSource, this.globalConfig);

        // 生成代码
        generator.generate();
    }

    /**
     * 数据库连接盒子
     */
    private static class ConnectionBox {
        private String ip;
        private int port;
        private String schemaName;
        private String username;
        private String password;
        private String url;

        private ConnectionBox(String ip, int port, String schemaName, String username, String password) {
            this.ip = ip;
            this.port = port;
            this.schemaName = schemaName;
            this.username = username;
            this.password = password;
            this.url = String.format("jdbc:mysql://%s:%d/%s?characterEncoding=utf-8&useInformationSchema=true", ip, port, schemaName);
        }

        private HikariDataSource createDataSource() {
            HikariDataSource dataSource = new HikariDataSource();
            dataSource.setJdbcUrl(this.url);
            dataSource.setUsername(this.username);
            dataSource.setPassword(this.password);
            return dataSource;
        }
    }
}
